#include"storage.h"
#include"memrw.h"
//#include "usb.h"
#include<dos.h>
#ifdef DEBUG
#include<stdio.h>
#endif
//
// reference spec:
//                Universal Serial Bus Mass Storage Class Bulk-Only Transport
//                Universal Serial Bus Mass Storage Specification For Bootability
//


DevDriverMethodType MassDriverMethod={&MassInitDev
							  	     	
};


///////////////                      *** bulk only transfer area ***//////////////////////////


//
//    ----------------> Ready           Command Block Wrapper (CBW) 
//    ^                  |              Command Status Wrapper (CSW)
//    |                  V
//    |                  
//    |       --------- CBW-------------
//    |       |          |             |
//    |       V          |             V
//    |                  |
//    |    Data-OUT      |           Data-IN
//    |       |          |             |
//    |       |          |             | 
//    |       |          |             |
//    |       |          V             |
//    |       --------> CSW <-----------
//    |                  |
//    <-------------------

static DWORD dWSignature=0;


/******************************************************************************
    Function:
        BOTTransation

    Description:
	process a BULK-ONLY Transport.

    Entry:
	MTPtr  -- command  data structure
	LengthPtr -- actual data transport length
	DevPtr -- Transport on the pointer device information  

    Exit:
        none
******************************************************************************/
void  BOTTransation(MassTransType * MTPtr, WORD * LengthPtr,DeviceInfoPtr DevPtr )
{
        

         //2. send CMD
#ifdef DEBUG
	 printf("BOT transaton CMD \n");
#endif
	  BYTE status=	 BOTCBW(MTPtr,DevPtr);

	 if(!status)
	 {		  
	          //2. transfer data
                 if(MTPtr->dDataSize)
                 {              
#ifdef DEBUG
	 printf("BOT transaton data \n");
#endif	  
                        BYTE DataStatus=BOTData( MTPtr,LengthPtr,DevPtr);
			   if(DataStatus & Stalled )	clearBulkEndpoint(MTPtr->bDirection,DevPtr);
			   if(DataStatus &(TimeOut |OtherError ))
			   {
			       BOTReset(DevPtr);
				return;  
			   }
			   			
                 }
		   //2. get status 	   
#ifdef DEBUG
	 printf("BOT transaton status \n");
#endif			   
		   status=BOTCSW( MTPtr,DevPtr);  
	 }
        else //error
        {
             //2. reset mass device
              BOTReset(DevPtr);
        }
       return;		
}


/******************************************************************************
    Function:
        BOTCBW

    Description:
	send CBW(Command Block Wrapper) on BULK transfer.

    Entry:
	MTPtr  -- command  data structure
	 CmdPtr  --- point to CBWType, shall set CBWCB, other field not.
                 DataPtr --- if have data to send or receive, point to data
                 bDirection --- bit 7: 1---in; 0---out
                 dDataSize --- size of data between CBW and CSW
                 bCmdSize --- size of CBW
	DevPtr -- Transport on the pointer device information  
	
    Exit:
       status:  0--ok
                   bit0: STALL
                   bit1: time out
                   bit2: error
******************************************************************************/
BYTE BOTCBW(MassTransType * MTPtr, DeviceInfoPtr DevPtr )
{
	CBWType * CMDPtr=(CBWType * ) MTPtr->CmdPtr;	
       DeviceInfoPtr DIPtr=DevPtr;
	
	CMDPtr->dCBWSignature=CBWSignature;
	CMDPtr->dCBWTag= ++ dWSignature;
	CMDPtr->dCBWDataTransferLength=MTPtr->dDataSize;
	CMDPtr->bmCBWFlags=MTPtr->bDirection;
	CMDPtr->bCBWLUN=((MassStorageType *)DIPtr->pDevData)->bLUN;
	CMDPtr->bCBWCBLength= MTPtr->bCmdSize - 15; 

	HCStrucType * HCPtr=DIPtr->pHCStrucPtr;
	ControllerAccessMethodType * CAMPtr=HCPtr->pHCDPointer;

       WORD length=0;
	return CAMPtr->bulkTransfer(DIPtr,0,MTPtr->CmdPtr,MTPtr->bCmdSize,&length);
                 
}


/******************************************************************************
    Function:
        BOTData

    Description:
	data phase on BULK transfer.

    Entry:
	MTPtr  -- command  data structure
	 CmdPtr  --- point to CBWType, shall set CBWCB, other field not.
                 DataPtr --- if have data to send or receive, point to data
                 bDirection --- bit 7: 1---in; 0---out
                 dDataSize --- size of data between CBW and CSW
                 bCmdSize --- size of CBW
	DevPtr -- Transport on the pointer device information  
	
    Exit:
       status:  0--ok
                   bit0: STALL
                   bit1: time out
                   bit2: error
******************************************************************************/
BYTE BOTData(MassTransType * MTPtr,WORD * LengthPtr,DeviceInfoPtr DIPtr)
{

      ControllerAccessMethodType * CAMPtr=DIPtr->pHCStrucPtr->pHCDPointer;

       BYTE status= CAMPtr->bulkTransfer(DIPtr,MTPtr->bDirection,MTPtr->DataPtr,MTPtr->dDataSize,LengthPtr);

	return status;   
}


/******************************************************************************
    Function:
        BOTCSW

    Description:
	send CSW(Command Status Wrapper) on BULK transfer.

    Entry:
	MTPtr  -- command  data structure
	 CmdPtr  --- point to CBWType, shall set CBWCB, other field not.
                 DataPtr --- if have data to send or receive, point to data
                 bDirection --- bit 7: 1---in; 0---out
                 dDataSize --- size of data between CBW and CSW
                 bCmdSize --- size of CBW
	DevPtr -- Transport on the pointer device information  
	
    Exit:
    status:
      00---command pass
      01---command failed
      02---phase error
      03---bulk get CSW error. may be STALL, time out, error.

      note: receive CSW in MassTransType.CmdPtr
******************************************************************************/
BYTE BOTCSW(MassTransType * MTPtr,DeviceInfoPtr DIPtr)
{    
      ControllerAccessMethodType * CAMPtr=DIPtr->pHCStrucPtr->pHCDPointer;
      WORD length=0;
      BYTE status;  
      BYTE ReturnStatus;	  

	
     for(int i=0; i<3; i++)// try 3 times
     {

      status=CAMPtr->bulkTransfer(DIPtr,0x80,MTPtr->CmdPtr,sizeof(CSWType),&length);
	if((status ==0) &&(length == sizeof(CSWType)))
	{

	       break;
	}
	else
	{
	   //3. if bulk transfer stall, clear bulk in endpoint.
	   
	    if(status & Stalled)
	    {
	              printf("clean in endpoint \n");//////////
	            	clearBulkEndpoint(0x80,DIPtr); // IN endpoint

	    }
	    else//3//other failed
	    {
    	              printf("clean IN endpoint \n");//////////
	           	clearBulkEndpoint(0x80,DIPtr); // IN endpoint
	              printf("clean OUT endpoint \n");//////////
	              clearBulkEndpoint(0x00,DIPtr); // OUT endpoint
	              break;
	    }

	}

      }


	 
     //2. if CSW error , reset mass device
     
      CSWType * CSWPtr=(CSWType * )MTPtr->CmdPtr;
      if(!status)
      	{
      	     if((CSWPtr->dCSWSignature !=CSWSignature ) || (CSWPtr->dCSWTag != dWSignature)  \
			 	|| (length != sizeof(CSWType)) ||  (CSWPtr->bCSWStatus >1)  )
      	     	{
      	     	       printf("CSW status error \n");/////////////
                     BOTReset(DIPtr);
			ReturnStatus= 3;
      	     	}
		else 	 ReturnStatus=CSWPtr->bCSWStatus;
      	}
	else   ReturnStatus=3; //error
      return ReturnStatus;
	  
}

/******************************************************************************
    Function:
        BOTReset

    Description:
	reset bulk endpoint.

    Entry:
	DevPtr -- Transport on the pointer device information  
	
    Exit:
	none

******************************************************************************/
void BOTReset(DeviceInfoPtr DIPtr)
{
      
       //2. reset mass storage device

	RequstFormatType  RFTPtr={0x21,0xff,0x00,0x00,0x00}; // refer to bulk only spec. class type request.
	RFTPtr.wIndex= DIPtr->bInterfaceNum;
	DIPtr->pHCStrucPtr->pHCDPointer->ControlTransfer(DIPtr,&RFTPtr);

	//2. clear bulk endpoint   
	clearBulkEndpoint(0x80,DIPtr); // IN endpoint
	clearBulkEndpoint(0x00,DIPtr); // OUT endpoint

	return;
}


/******************************************************************************
    Function:
        clearBulkEndpoint

    Description:
	set CLEAR FEATURE on bulk endpointer.

    Entry:
       direction  -- bit7=0    OUT bulk; bit7=1    IN bulk
	DevPtr -- Transport on the pointer device information  
	
    Exit:
	none

******************************************************************************/
void clearBulkEndpoint(BYTE direction,DeviceInfoPtr DIPtr)
{
       
	RequstFormatType RFTPtr={0x02,CLEAR_FEATURE,0,0,0};
	if(direction & 0x80)RFTPtr.wIndex= DIPtr->bBulkInEndpoint;
	else RFTPtr.wIndex= DIPtr->bBulkOutEndpoint;

	DIPtr->pHCStrucPtr->pHCDPointer->ControlTransfer(DIPtr,&RFTPtr);

	//2. reset toggle bit  , usage not clear

	if(direction & 0x80)DIPtr->bDataSync &= 0xfe;   //usage not clear 
	else DIPtr->bDataSync &= 0xfd;

	return;

}




///////////////////////////BOT END////////////////////////////////////////////



///////////////////////////	Control/Bulk/Interrupt(CBI) Transport area///////
//TBD

///////////////////////////////CBI END/////////////////////////////////




///////////////////////////////MASS Storage area//////////////////////////

/******************************************************************************
    Function:
        MassTransation

    Description:
	process a mass storage transport.

    Entry:
	MTPtr  -- command  data structure
	LengthPtr -- actual data transport length
	DevPtr -- Transport on the pointer device information  

    Exit:
        none
******************************************************************************/
void  MassTransation(MassTransType * MTPtr, WORD * LengthPtr,DeviceInfoPtr DevPtr)
{
         //2. BOT
        // BULK ONLY DEVICE
        if(DevPtr ->bProtocolCode == 0x50)//refer to Mass Storage Class Specification Overview
        	{
		        BOTTransation(MTPtr,  LengthPtr,DevPtr);
        	}
         //2. CBI
         //CONTROL BULK INTERRUPT DEVICE
 
         if((DevPtr ->bProtocolCode == 00) ||(DevPtr ->bProtocolCode == 01) )       
         	{
         	        //not ready.
         	}
	  return;	 
}



/******************************************************************************
    Function:
        MassINQUIRY

    Description:
	 send a INQUIRY command to storage device.

    Entry:
	DataPtr  --data buffer pointer to INQUIRY data	
	DevPtr -- Transport on the pointer device information  

    Exit:
        none
******************************************************************************/
void MassINQUIRY(BYTE * DataPtr,DeviceInfoPtr DevPtr)
{

//         MassTransType
//                 CmdPtr  --- point to CBWType, shall set CBWCB, other field not.
//                 DataPtr --- if have data to send or receive, point to data
//                  bDirection --- bit 7: 1---in; 0---out
//                 dDataSize --- size of data between CBW and CSW
//                 bCmdSize --- size of CBW

         MassTransType MTT;

	  CBWType CBW;
         cleanBuffer((BYTE *)&CBW, sizeof(CBWType));
         BYTE *ptr=CBW.CBWCB;
	  ptr[0]=INQUIRY; //refer to Universal Serial Bus Mass Storage Specification For Bootability	 
	  ptr[4]=0x24; //INQUIRY cmd

	  MTT.CmdPtr= (BYTE *)&CBW;
	  MTT.DataPtr=DataPtr;
	  MTT.bDirection=DATAIN;//DATA IN
	  MTT.dDataSize=0x24;
	  MTT.bCmdSize=15+12;
	  	 
         WORD length;
	  MassTransation(&MTT, &length,DevPtr);
	  return;
}


/******************************************************************************
    Function:
        MassRequestSense

    Description:
	 The REQUEST SENSE command instructs the Device to transfer sense data to the host computer.

    Entry:
	DevPtr -- Transport on the pointer to device information  

    Exit:
        SenseKey--The Sense Key provides generic categories in which error and exception conditions 
        can be reported.
	 BIT 16:23 :ADDITIONAL SENSE CODE QUALIFIER
        BIT  8:15: ADDITIONAL SENSE CODE
        BIT 0:7:    SENSE KEY
******************************************************************************/
DWORD MassRequestSense(DeviceInfoPtr DevPtr)
{


         MassTransType MTT;
         BYTE Data[0x20];
	  BYTE * DataPtr=Data;	 
		  
	  CBWType CBW;
         cleanBuffer((BYTE *)&CBW, sizeof(CBWType));
         BYTE *ptr=CBW.CBWCB;
	  ptr[0]=REQUESTSense; //refer to Universal Serial Bus Mass Storage Specification For Bootability	 
	  ptr[4]=0x12; //INQUIRY cmd


         
	  MTT.CmdPtr= (BYTE *)&CBW;
	  MTT.DataPtr=DataPtr;
	  MTT.bDirection=DATAIN;//DATA IN
	  MTT.dDataSize=0x12;
	  MTT.bCmdSize=15+12;
	  	 
         WORD length;
	  MassTransation(&MTT, &length,DevPtr);

	  DWORD SenseKey;
	  SenseKey = DataPtr[13];
	  SenseKey <<=8;
	  SenseKey +=DataPtr[12];
	  SenseKey <<=8;
	  SenseKey += DataPtr[2];

	  return SenseKey;
}



/******************************************************************************
    Function:
        MassDevReady

    Description:
	 The TEST UNIT READY command provides a means to check if the Device is ready.

    Entry:
	DevPtr -- Transport on the pointer to device information  

    Exit:
	none
******************************************************************************/
void MassDevReady(DeviceInfoPtr DevPtr)
{
          for(int i=0;i<3;i++)
          {
                  if(MassTestUnitReady(DevPtr) ==0) break;
		    else
		    {
		         delay(50); //wait for ready
		    }
          }
}


/******************************************************************************
    Function:
        MassTestUnitReady

    Description:
	 The TEST UNIT READY command provides a means to check if the Device is ready.

    Entry:
	DevPtr -- Transport on the pointer to device information  

    Exit:
        SenseKey--The Sense Key provides generic categories in which error and exception conditions 
        can be reported.
	 BIT 16:23 :ADDITIONAL SENSE CODE QUALIFIER
        BIT  8:15: ADDITIONAL SENSE CODE
        BIT 0:7:    SENSE KEY
******************************************************************************/
DWORD MassTestUnitReady(DeviceInfoPtr DevPtr)
{


         MassTransType MTT;

	  CBWType CBW;
         cleanBuffer((BYTE *)&CBW, sizeof(CBWType));
         BYTE *ptr=CBW.CBWCB;
	  ptr[0]=TESTUnitReady; //refer to Universal Serial Bus Mass Storage Specification For Bootability	 


	  MTT.CmdPtr= (BYTE *)&CBW;
	  MTT.bDirection=DATAIN;//DATA IN
	  MTT.dDataSize=0;
	  MTT.bCmdSize=15+12;
	  	 
         WORD length;
	  MassTransation(&MTT, &length,DevPtr);

	  return MassRequestSense(DevPtr);
}








/******************************************************************************
    Function:
        MassREADCapacity

    Description:
	 The READ CAPACITY command provides a means for the host computer to request information regarding
	the capacity of the installed medium of the device..

    Entry:
       DataPtr -- getted data buffer 
	DevPtr -- Transport on the pointer to device information  

    Exit:
	none
******************************************************************************/
void MassREADCapacity(BYTE * DataPtr,DeviceInfoPtr DevPtr)
{


         MassTransType MTT;

	  CBWType CBW;
         cleanBuffer((BYTE *)&CBW, sizeof(CBWType));
         BYTE *ptr=CBW.CBWCB;
	  ptr[0]=READCAPACITY; //refer to Universal Serial Bus Mass Storage Specification For Bootability	 
	  

	  MTT.CmdPtr= (BYTE *)&CBW;
	  MTT.DataPtr=DataPtr;
	  MTT.bDirection=DATAIN;//DATA IN
	  MTT.dDataSize=8;
	  MTT.bCmdSize=15+12;
	  	 
         WORD length;
	  MassTransation(&MTT, &length,DevPtr);

	  return;
}



/******************************************************************************
    Function:
        MassFORMAT

    Description:
	The host sends the FORMAT UNIT command to physically format a diskette according to selected options.

    Entry:      
	DevPtr -- Transport on the pointer to device information  

    Exit:
	none
******************************************************************************/
void MassFORMAT(DeviceInfoPtr DevPtr)
{
//         MassTransType
//                 CmdPtr  --- point to CBWType, shall set CBWCB, other field not.
//                 DataPtr --- if have data to send or receive, point to data
//                  bDirection --- bit 7: 1---in; 0---out
//                 dDataSize --- size of data between CBW and CSW
//                 bCmdSize --- size of CBW

         MassTransType MTT;

	  CBWType CBW;
         cleanBuffer((BYTE *)&CBW, sizeof(CBWType));
         BYTE *ptr=CBW.CBWCB;
	  ptr[0]=0x4; //refer to Universal Serial Bus Mass Storage Specification For Bootability	
	  ptr[1]=0x17;
	  

	  MTT.CmdPtr= (BYTE *)&CBW;
//	  MTT.DataPtr=DataPtr;
	  MTT.bDirection=DATAIN;//DATA IN
	  MTT.dDataSize=0;
	  MTT.bCmdSize=15+12;
	  	 
         WORD length;
	  MassTransation(&MTT, &length,DevPtr);

	  return;
}


/******************************************************************************
    Function:
        MassREAD10

    Description:
	 The READ(10) command requests that the Device transfer data to the host computer.

    Entry:
       LBA --logical block address
       BlockLength --specifies the number of contiguous logical blocks of data that are transferred in bytes
       DataPtr -- getted data buffer 
	DevPtr -- Transport on the pointer to device information  

    Exit:
	none
******************************************************************************/
void MassREAD10(DWORD LBA,	WORD BlockLength,BYTE * DataPtr,DeviceInfoPtr DevPtr)
{


         MassDevReady(DevPtr) ;

         MassTransType MTT;

	  CBWType CBW;
         cleanBuffer((BYTE *)&CBW, sizeof(CBWType));
         BYTE *ptr=CBW.CBWCB;
	  ptr[0]=READ10; //refer to Universal Serial Bus Mass Storage Specification For Bootability	 
	  ptr[2]=LBA>>24 ;  //little endian to midlle endian 
	  ptr[3]=LBA>>16	;  
	  ptr[4]=LBA>>8;
	  ptr[5]=LBA	  	;	
	  
	  ptr[7]=BlockLength >>8;
	  ptr[8]=BlockLength;

	  MTT.CmdPtr= (BYTE *)&CBW;
	  MTT.DataPtr=DataPtr;
	  MTT.bDirection=DATAIN;//DATA IN
	  MTT.dDataSize=BlockLength * 512; //temp  not known size of block.
	  MTT.bCmdSize=15+12;
	  	 
         WORD length;
	  MassTransation(&MTT, &length,DevPtr);

	  return;
}


/******************************************************************************
    Function:
        MassWRITE10

    Description:
	The WRITE(10) command requests that the Device write the data transferred by the Host Computer to the
	medium.

    Entry:      
       LBA --logical block address
       BlockLength --specifies the number of contiguous logical blocks of data that are transferred in bytes
       DataPtr --  data buffer to be write. 
	DevPtr -- Transport on the pointer to device information  

    Exit:
	none
******************************************************************************/
void MassWRITE10(DWORD LBA,	WORD BlockLength,BYTE * DataPtr,DeviceInfoPtr DevPtr)
{


         MassDevReady(DevPtr);

         MassTransType MTT;

	  CBWType CBW;
         cleanBuffer((BYTE *)&CBW, sizeof(CBWType));
         BYTE *ptr=CBW.CBWCB;
	  ptr[0]=0x2A; //refer to Universal Serial Bus Mass Storage Specification For Bootability	 
	  ptr[1]=0x0a;
	  ptr[2]=LBA>>24 ;  //little endian to midlle endian 
	  ptr[3]=LBA>>16	;  
	  ptr[4]=LBA>>8;
	  ptr[5]=LBA	  	;	
	  
	  ptr[7]=BlockLength >>8;
	  ptr[8]=BlockLength;

	  MTT.CmdPtr= (BYTE *)&CBW;
	  MTT.DataPtr=DataPtr;
	  MTT.bDirection=DATAOUT;//DATA out
	  MTT.dDataSize=BlockLength * 512; //temp  not known size of block.
	  MTT.bCmdSize=15+12;
	  	 
         WORD length;
	  MassTransation(&MTT, &length,DevPtr);

	  return;
}


void MassInitDev(void * ptr)
{
	   DeviceInfoPtr DevPtr = (DeviceInfoPtr)ptr;
          BYTE data[64];
	   MassINQUIRY(data,DevPtr);

	   MassStorageType  *pMS=(MassStorageType *)allocAlignMem(sizeof( MassStorageType),1);
	   DevPtr->pDevData=pMS;

	   pMS->PDT=data[0] & 0x1f; //refer to USB boot spec, or SPC inquiry cmd.
	   memCopy(pMS->VendorID, &data[8], 8);
	   memCopy(pMS->ProductID, &data[16], 16);
	   memCopy(pMS->ProductVL, &data[32], 4);	   


          MassREADCapacity(data,DevPtr);
	   DWORD temp=0;

	   temp=data[0];//middle endian change to little endian
	   temp<<=8;
	   temp+=data[1];
	   temp<<=8;
	   temp+=data[2];
	   temp<<=8;
	   temp+=data[3];
	   
          pMS->LastLBA=temp;

	   temp=data[4];
	   temp<<=8;
	   temp+=data[5];
	   temp<<=8;
	   temp+=data[6];
	   temp<<=8;
	   temp+=data[7];		  
	   pMS->BlockLength=temp;	 

//get LUN
           RequstFormatType LUNRequest={0xA1,0xFE,0,0,1};//refer to mass bulk-only transport section 3.2   
           ControllerAccessMethodType * pDT=(ControllerAccessMethodType *)DevPtr->pHCStrucPtr->pDescriptorPtr;
	    pDT->ControlTransfer(DevPtr, &LUNRequest);	   
	    BYTE * pBuffer= pDT->pointToDevBuffer(DevPtr);	
	    pMS->bLUN=pBuffer[0];

		
}

/******************************************************************************
    Function:
        getMassDevMethod

    Description:
	return mass device driver  interface.

    Entry:
	none

    Exit:
        return pointer to mass device driver 
******************************************************************************/
DevDriverMethodType * getMassDevMethod()
{
	return &MassDriverMethod;
}

